<?php
/**
 * <b>RUBIK IT Solution & Development ltd.</b>
 *
 * PROJECT		: Rubik Ultimate Framework
 * @version 	: 1.1
 * COPYRIGHT    : 2008
 * ------------------------------------------------------
 * 
 * Created on	:  Jul 1, 2008
 * 
 */

// no direct access
defined ( '_FW_EXEC' ) or die ( 'Restricted access' );
RLibImporter::cls( "xml.JSimpleXMLElement" );
RCoreImporter::cls( "html.BaseMenu" );

class JSimpleXML {
	/**
	 * The XML parser
	 *
	 * @var resource
	 */
	var $_parser = null;
	
	/**
	 * The XML document
	 *
	 * @var string
	 */
	var $_xml = '';
	
	/**
	 * Document element
	 *
	 * @var object
	 */
	var $document = null;
	
	/**
	 * Current object depth
	 *
	 * @var array
	 */
	var $_stack = array ();
	
	var $_level = 0;
	
	/**
	 * Constructor.
	 *
	 * @access protected
	 */
	function __construct($options = null) {
		if (! function_exists ( 'xml_parser_create' )) {
			RError::raiseError ( 301, "function: <b>xml_parser_create<> isn't supported." );
			return false;
		}
		//Create the parser resource and make sure both versions of PHP autodetect the format.
		$this->_parser = xml_parser_create ( '' );
		
		// check parser resource
		xml_set_object ( $this->_parser, $this );
		xml_parser_set_option ( $this->_parser, XML_OPTION_CASE_FOLDING, 0 );
		if (is_array ( $options )) {
			foreach ( $options as $option => $value ) {
				xml_parser_set_option ( $this->_parser, $option, $value );
			}
		}
		
		//Set the handlers
		xml_set_element_handler ( $this->_parser, '_startElement', '_endElement' );
		xml_set_character_data_handler ( $this->_parser, '_characterData' );
	}
	
	/**
	 * Interprets a string of XML into an object
	 *
	 * This function will take the well-formed xml string data and return an object of class
	 * JSimpleXMLElement with properties containing the data held within the xml document.
	 * If any errors occur, it returns FALSE.
	 *
	 * @param string  Well-formed xml string data
	 * @param string  currently ignored
	 * @return object JSimpleXMLElement
	 */
	function loadString($string, $classname = null) {
		$this->_parse ( $string );
		return true;
	}
	
	/**
	 * Interprets an XML file into an object
	 *
	 * This function will convert the well-formed XML document in the file specified by filename
	 * to an object  of class JSimpleXMLElement. If any errors occur during file access or
	 * interpretation, the function returns FALSE.
	 *
	 * @param string  Path to xml file containing a well-formed XML document
	 * @param string  currently ignored
	 * @return boolean True if successful, false if file empty
	 */
	function loadFile($path, $level = 0) {
		$this->_level = $level;
		//Check to see of the path exists
		if (! file_exists ( $path )) {
			RError::raiseWarning ( 301, "The XML file: <b>$path</b> doesn't exist!" );
			return false;
		}
		
		//Get the XML document loaded into a variable
		$xml = trim ( file_get_contents ( $path ) );
		if ($xml == '') {
			return false;
		} else {
			$this->_parse ( $xml );
			return true;
		}
	}
	
	/**
	 * Get a JSimpleXMLElement object from a DOM node.
	 *
	 * This function takes a node of a DOM  document and makes it into a JSimpleXML node.
	 * This new object can then be used as a native JSimpleXML element. If any errors occur,
	 * it returns FALSE.
	 *
	 * @param string	DOM  document
	 * @param string   	currently ignored
	 * @return object 	JSimpleXMLElement
	 */
	function importDOM($node, $classname = null) {
		return false;
	}
	
	/**
	 * Get the parser
	 *
	 * @access public
	 * @return resource XML parser resource handle
	 */
	function getParser() {
		return $this->_parser;
	}
	
	/**
	 * Set the parser
	 *
	 * @access public
	 * @param resource	XML parser resource handle
	 */
	function setParser($parser) {
		$this->_parser = $parser;
	}
	
	/**
	 * Start parsing an XML document
	 *
	 * Parses an XML document. The handlers for the configured events are called as many times as necessary.
	 *
	 * @param $xml 	string 	data to parse
	 */
	function _parse($data = '') {
		//Error handling
		if (! xml_parse ( $this->_parser, $data )) {
			$this->_handleError ( xml_get_error_code ( $this->_parser ), xml_get_current_line_number ( $this->_parser ), xml_get_current_column_number ( $this->_parser ) );
		}
		
		//Free the parser
		xml_parser_free ( $this->_parser );
	}
	
	/**
	 * Handles an XML parsing error
	 *
	 * @access protected
	 * @param int $code XML Error Code
	 * @param int $line Line on which the error happened
	 * @param int $col Column on which the error happened
	 */
	function _handleError($code, $line, $col) {
		RError::raiseWarning ( 'SOME_ERROR_CODE', 'XML Parsing Error at ' . $line . ':' . $col . '. Error ' . $code . ': ' . xml_error_string ( $code ) );
	}
	
	/**
	 * Gets the reference to the current direct parent
	 *
	 * @return object
	 */
	function _getStackLocation() {
		$return = '';
		foreach ( $this->_stack as $stack ) {
			$return .= $stack . '->';
		}
		
		return rtrim ( $return, '->' );
	}
	
	/**
	 * Handler function for the start of a tag
	 *
	 * @access protected
	 * @param resource $parser
	 * @param string $name
	 * @param array $attrs
	 */
	function _startElement($parser, $name, $attrs = array()) {
		//Check to see if tag is root-level
		if (count ( $this->_stack ) == 0) {
			//If so, set the document as the current tag
			//$classname = get_class( $this ) . 'Element';
			$this->document = new JSimpleXMLElement ( $name, $attrs, $this->_level );
			
			//And start out the stack with the document tag
			$this->_stack = array ('document' );
		} //If it isn't root level, use the stack to find the parent
else {
			//Get the name which points to the current direct parent, relative to $this
			$parent = $this->_getStackLocation ();
			
			$level = $this->_level + count ( $this->_stack );
			eval ( '$this->' . $parent . '->addChild($name, $attrs, ' . $level . ');' );
			
			//Update the stack
			eval ( '$this->_stack[] = $name.\'[\'.(count($this->' . $parent . '->' . $name . ') - 1).\']\';' );
		
		}
	}
	
	/**
	 * Handler function for the end of a tag
	 *
	 * @access protected
	 * @param resource $parser
	 * @param string $name
	 */
	function _endElement($parser, $name) {
		//Update stack by removing the end value from it as the parent
		array_pop ( $this->_stack );
	}
	
	/**
	 * Handler function for the character data within a tag
	 *
	 * @access protected
	 * @param resource $parser
	 * @param string $data
	 */
	function _characterData($parser, $data) {
		//Get the reference to the current parent object
		$tag = $this->_getStackLocation ();
		
		//Assign data to it
		eval ( '$this->' . $tag . '->_data .= $data;' );
	}
}

?>